package org.byd.dmsja.utils;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.Collator;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * SNE - 最常用实用程序。
 */
public class SNE {

    /**
     * 获取错误信息。确保不为空。
     */
    public static String getErrMsg(Exception ex) {
        var ret = "没有提供错误信息的错误。";
        if (ex != null) {
            ret = ex.getMessage();
            ret = (ret == null || ret.trim().isEmpty()) ? ex.toString() : ret;
        }
        return ret;
    }

    /**
     * 是否为null。
     */
    public static Boolean isNull(Object src) {
        return Objects.isNull(src);
    }

    /**
     * 是否为空。
     */
    public static Boolean isEmpty(String src) {
        return src == null || src.isEmpty();
    }

    /**
     * 是否为null或空或全空。
     */
    public static Boolean isBlank(String src) {
        return src == null || src.isBlank();
    }

    /**
     * 空值替换。
     */
    public static <T> T nullElse(T src, T def) {
        return src == null ? def : src;
    }

    /**
     * 字符串空值替换。
     */
    public static String emptyElse(String src, String def) {
        return (!(src == null || src.isEmpty())) ? src : def;
    }

    /**
     * 字符串全空值替换。
     */
    public static String blankElse(String src, String def) {
        return (!(src == null || src.isBlank())) ? src : def;
    }

    /**
     * 确保值在范围内。
     */
    public static <T extends Comparable<T>> T forceIn(T value, T min, T max) {
        var ret = value;
        if (compare(min, max) > 0) {
            var tmp = min;
            min = max;
            max = tmp;
        }
        ret = (compare(ret, min) < 0) ? min : ret;
        ret = (compare(ret, max) > 0) ? max : ret;
        return ret;
    }

    /**
     * 安全匹字符串。如果匹配不成功返回null。
     */
    public static String parseString(Object value) {
        return parseString(value, null);
    }

    /**
     * 安全匹字符串。如果匹配不成功返回def。
     */
    public static String parseString(Object value, String def) {
        var ret = def;
        if (value != null) {
            if (value.getClass() == String.class) {
                ret = (String) value;
            } else {
                try {ret = value.toString();} catch (Exception ignored) {}
            }
        }
        return ret;
    }

    /**
     * 安全匹配整数。如果匹配不成功返回null。
     */
    public static Integer parseInteger(Object value) {
        return parseInteger(value, null);
    }

    /**
     * 安全匹配整数。如果匹配不成功返回def。
     */
    public static Integer parseInteger(Object value, Integer def) {
        var ret = def;
        if (value != null) {
            if (value.getClass() == Integer.class) {
                ret = (Integer) value;
            } else {
                try {ret = Math.round(Float.parseFloat(value.toString()));} catch (Exception ignored) {}
            }
        }
        return ret;
    }

    /**
     * 安全匹配长整数。如果匹配不成功返回null。
     */
    public static Long parseLong(Object value) {
        return parseLong(value, null);
    }

    /**
     * 安全匹配长整数。如果匹配不成功返回def。
     */
    public static Long parseLong(Object value, Long def) {
        var ret = def;
        if (value != null) {
            if (value.getClass() == Long.class) {
                ret = (Long) value;
            } else {
                try {ret = Math.round(Double.parseDouble(value.toString()));} catch (Exception ignored) {}
            }
        }
        return ret;
    }

    /**
     * 安全匹配实数。如果匹配不成功返回null。
     */
    public static Double parseDouble(Object value) {
        return parseDouble(value, null);
    }

    /**
     * 安全匹配实数。如果匹配不成功返回def。
     */
    public static Double parseDouble(Object value, Double def) {
        var ret = def;
        if (value != null) {
            if (value.getClass() == Double.class) {
                ret = (Double) value;
            } else {
                try {ret = Double.parseDouble(value.toString());} catch (Exception ignored) {}
            }
        }
        return ret;
    }

    /**
     * 安全匹配浮点值。如果匹配不成功返回null。
     */
    public static Float parseFloat(Object value) {
        return parseFloat(value, null);
    }

    /**
     * 安全匹配浮点值。如果匹配不成功返回def。
     */
    public static Float parseFloat(Object value, Float def) {
        var ret = def;
        if (value != null) {
            if (value.getClass() == Float.class) {
                ret = (Float) value;
            } else {
                try {ret = Float.parseFloat(value.toString());} catch (Exception ignored) {}
            }
        }
        return ret;
    }

    /**
     * 安全匹配布尔值。如果匹配不成功返回null。
     */
    public static Boolean parseBoolean(Object value) {
        return parseBoolean(value, null);
    }

    /**
     * 安全匹配布尔值。如果匹配不成功返回def。
     */
    public static Boolean parseBoolean(Object value, Boolean def) {
        var ret = def;
        if (value != null) {
            if (value.getClass() == Boolean.class) {
                ret = (Boolean) value;
            } else {
                try {ret = Boolean.parseBoolean(value.toString());} catch (Exception ignored) {}
            }
        }
        return ret;
    }

    /**
     * 暴力匹配布尔值。value值是[null,空,全空,0,false,no,off,假,否,关,无](大小写无关)其中之一返回false，其余返回true。
     */
    public static Boolean parseBooleanEx(Object value) {
        var ret = true;
        if (value == null) {
            ret = false;
        } else {
            var txt = value.toString().trim();
            if (txt.isEmpty()) {ret = false;} else {
                ret = !Arrays.asList("0,false,no,off,not,假,否,关,无".split(",", -1)).contains(txt);
            }
        }
        return ret;
    }

    /**
     * 安全匹配日期值。如果匹配不成功返回null。
     */
    public static Date parseDate(Object value) {
        return parseDate(value, null);
    }

    /**
     * 安全匹配日期值。如果匹配不成功返回def。
     */
    public static Date parseDate(Object value, Date def) {
        var ret = def;
        if (value != null) {
            if (value.getClass() == Date.class) {
                ret = (Date) value;
            } else {
                ret = parseDate_Sub(value.toString(), def);
            }
        }
        return ret;
    }

    private static Date parseDate_Sub(String anyDateTimeLikeStr, Date def) {
        var ret = def;
        try {
            // Split java.util.Date and Time Part
            var datePart = "";
            var timePart = "";
            var src = anyDateTimeLikeStr.trim();
            if (!src.isEmpty()) {
                src = (src.endsWith("z") || src.endsWith("Z")) ? src.substring(0, src.length() - 1) : src;
                src = src.replaceAll("\\s*[年月\\-/]\\s*", "/");
                src = src.replaceAll("\\s*日\\s*", " ");
                src = src.replaceAll("\\s*[时点分:]\\s*", ":");
                src = src.replaceAll("\\s*秒\\s*", "");
                src = src.replaceAll("\\s*[.]\\s*", ".");
                src = src.replaceAll("\\s+", "T");
                src = src.replaceAll("\\s*[tT]\\s*", "T");
                var parts = src.split("T");
                if (parts.length <= 1) {
                    if (parts[0].indexOf(":") > 0) {
                        datePart = "";
                        timePart = parts[0];
                    } else {
                        datePart = parts[0];
                        timePart = "";
                    }
                } else {
                    datePart = parts[0];
                    timePart = parts[1];
                }
            }
            // Init caResult
            var caResult = Calendar.getInstance();
            caResult.set(Calendar.HOUR_OF_DAY, 0);
            caResult.set(Calendar.MINUTE, 0);
            caResult.set(Calendar.SECOND, 0);
            caResult.set(Calendar.MILLISECOND, 0);
            // parse java.util.Date Part
            var parseDatePartOkIf = false;
            if (!datePart.isEmpty()) {
                {
                    var rgx = Pattern.compile("(\\d+)/(\\d+)/(\\d+)");
                    var ma = rgx.matcher(datePart);
                    if (ma.find()) {
                        if (ma.group().equals(datePart)) {
                            parseDatePartOkIf = true;
                            caResult.set(Calendar.YEAR, Integer.parseInt(ma.group(1)));
                            caResult.set(Calendar.MONTH, Integer.parseInt(ma.group(2)) - 1);
                            caResult.set(Calendar.DAY_OF_MONTH, Integer.parseInt(ma.group(3)));
                        }
                    }
                }
                if (!parseDatePartOkIf) {
                    var rgx = Pattern.compile("(\\d+)/(\\d+)");
                    var ma = rgx.matcher(datePart);
                    if (ma.find()) {
                        parseDatePartOkIf = true;
                        caResult.set(Calendar.MONTH, Integer.parseInt(ma.group(1)) - 1);
                        caResult.set(Calendar.DAY_OF_MONTH, Integer.parseInt(ma.group(2)));
                    }
                }
            }
            // parse Time Part
            var parseTimePartOkIf = false;
            if (!timePart.isEmpty()) {
                {
                    var rgx = Pattern.compile("(\\d+):(\\d+):(\\d+).(\\d+)");
                    var ma = rgx.matcher(timePart);
                    if (ma.find()) {
                        if (ma.group().equals(timePart)) {
                            parseTimePartOkIf = true;
                            caResult.set(Calendar.HOUR_OF_DAY, Integer.parseInt(ma.group(1)));
                            caResult.set(Calendar.MINUTE, Integer.parseInt(ma.group(2)));
                            caResult.set(Calendar.SECOND, Integer.parseInt(ma.group(3)));
                            caResult.set(Calendar.MILLISECOND, Integer.parseInt(ma.group(4)));
                        }
                    }
                }
                if (!parseTimePartOkIf) {
                    var rgx = Pattern.compile("(\\d+):(\\d+):(\\d+)");
                    var ma = rgx.matcher(timePart);
                    if (ma.find()) {
                        if (ma.group().equals(timePart)) {
                            parseTimePartOkIf = true;
                            caResult.set(Calendar.HOUR_OF_DAY, Integer.parseInt(ma.group(1)));
                            caResult.set(Calendar.MINUTE, Integer.parseInt(ma.group(2)));
                            caResult.set(Calendar.SECOND, Integer.parseInt(ma.group(3)));
                        }
                    }
                }
                if (!parseTimePartOkIf) {
                    var rgx = Pattern.compile("(\\d+):(\\d+)");
                    var ma = rgx.matcher(timePart);
                    if (ma.find()) {
                        if (ma.group().equals(timePart)) {
                            parseTimePartOkIf = true;
                            caResult.set(Calendar.HOUR_OF_DAY, Integer.parseInt(ma.group(1)));
                            caResult.set(Calendar.MINUTE, Integer.parseInt(ma.group(2)));
                        }
                    }
                }
                if (!parseTimePartOkIf) {
                    var rgx = Pattern.compile("(\\d+)");
                    var ma = rgx.matcher(timePart);
                    if (ma.find()) {
                        if (ma.group().equals(timePart)) {
                            parseTimePartOkIf = true;
                            caResult.set(Calendar.HOUR_OF_DAY, Integer.parseInt(ma.group(1)));
                        }
                    }
                }
            }
            // Final
            var parseOkIf = parseDatePartOkIf || parseTimePartOkIf;
            if ((parseOkIf) && (!datePart.isEmpty()) && (!parseDatePartOkIf)) {
                parseOkIf = false;
            }
            if ((parseOkIf) && (!timePart.isEmpty()) && (!parseTimePartOkIf)) {
                parseOkIf = false;
            }
            if (parseOkIf) {
                ret = caResult.getTime();
            }
        } catch (Exception ignored) {
        }
        return ret;
    }

    /**
     * 枚举类型。
     */
    public static <T extends Enum<T>> T parseEnum(Object value, Class<T> enumCls) {
        return parseEnum(value, enumCls, null);
    }

    /**
     * 枚举类型。
     */
    public static <T extends Enum<T>> T parseEnum(Object value, Class<T> enumCls, T def) {
        T ret = def;
        if (value != null) {
            for (T ec : enumCls.getEnumConstants()) {
                if (ec.name().equalsIgnoreCase(value.toString().trim())) {ret = ec;}
            }
        }
        return ret;
    }

    /**
     * 值比较。可以处理null值，null默认最小。
     */
    public static <T> Integer compare(T v1, T v2) {
        int xRet;
        if (v1 == null) {
            if (v2 == null) {
                xRet = 0;
            } else {
                xRet = -1;
            }
        } else {
            if (v2 == null) {
                xRet = 1;
            } else {
                if (v1.getClass() == String.class) {
                    xRet = Collator.getInstance(Locale.CHINA).compare(v1, v2);
                } else {
                    xRet = ((Comparable<T>) v1).compareTo(v2);
                }
            }
        }
        xRet = xRet < 0 ? -1 : xRet;
        xRet = xRet > 0 ? 1 : xRet;
        return xRet;
    }

    /**
     * 值比较。不是倒序：null是视为最小。
     */
    public static Integer compareEx(Object v1, Object v2) {
        return compareEx(v1, v2, false, true);
    }

    /**
     * 值比较。是否倒序由isDescending决定：null是否视为最小。
     */
    public static Integer compareEx(Object v1, Object v2, Boolean isDescending) {
        return compareEx(v1, v2, isDescending, true);
    }

    /**
     * 值比较。isDescending为false时：v1小于v2返回-1;  v1等于v2返回0；v1大于v2返回1。
     */
    public static Integer compareEx(Object v1, Object v2, Boolean isDescending, Boolean isNullLeast) {
        Integer ret;
        BiFunction<String, String, Integer>
                fnCmpStr = (s1, s2) -> Collator.getInstance(Locale.CHINA).compare(s1, s2);
        if (v1 == null) {
            if (v2 == null) {
                ret = 0;
            } else {
                ret = isNullLeast ? 1 : -1;
            }
        } else {
            if (v2 == null) {
                ret = isNullLeast ? -1 : 1;
            } else {
                if (v1.getClass() == v2.getClass()) {
                    if (v1 instanceof String) {
                        ret = fnCmpStr.apply((String) v1, (String) v2);
                    } else if (v1 instanceof Comparable<?>) {
                        ret = ((Comparable<Object>) v1).compareTo(v2);
                    } else {
                        ret = fnCmpStr.apply(v1.toString(), v2.toString());
                    }
                } else {
                    ret = fnCmpStr.apply(v1.toString(), v2.toString());
                }
            }
        }
        ret = isDescending ? -ret : ret;
        return ret;
    }

    /**
     * 求和。
     */
    public static <TValue> TValue sum(List<TValue> valueList) {
        Class<?> t = null;
        var n = 0.0;
        var s = 0.0;
        if (valueList != null && !valueList.isEmpty()) {
            for (var value : valueList) {
                if (value instanceof Number) {
                    t = value.getClass();
                    n = n + 1.0;
                    s = s + ((Number) value).doubleValue();
                }
            }
        }
        var ret = (TValue) castDblAsNumber(s, t);
        return ret;
    }

    /**
     * 求平均。
     */
    public static <TValue> TValue avg(List<TValue> valueList) {
        Class<?> t = null;
        var n = 0.0;
        var s = 0.0;
        var g = 0.0;
        if (valueList != null && !valueList.isEmpty()) {
            for (var value : valueList) {
                if (value instanceof Number) {
                    t = value.getClass();
                    n = n + 1.0;
                    s = s + ((Number) value).doubleValue();
                }
            }
            g = n > 0.0 ? s / n : 0.0;
        }
        var ret = (TValue) castDblAsNumber(g, t);
        return ret;
    }

    /**
     * Double转换为Number。
     */
    public static <TNumber> TNumber castDblAsNumber(Double dbl, Class<TNumber> tCls) {
        TNumber ret = null;
        if (tCls == Double.class) {
            ret = (TNumber) dbl;
        } else if (tCls == Float.class) {
            ret = (TNumber) Float.valueOf(dbl.floatValue());
        } else if (tCls == Long.class) {
            ret = (TNumber) Long.valueOf(dbl.longValue());
        } else if (tCls == Integer.class) {
            ret = (TNumber) Integer.valueOf(dbl.intValue());
        } else if (tCls == Short.class) {
            ret = (TNumber) Short.valueOf(dbl.shortValue());
        } else if (tCls == Byte.class) {
            ret = (TNumber) Byte.valueOf(dbl.byteValue());
        }
        return ret;
    }

    /**
     * 不带分隔符的字符串拼接，null视为空，不会返回null。
     */
    public static String concat(Object... values) {
        return concatWs("", values);
    }

    /**
     * 带分隔符的字符串拼接，null视为空，不会返回null。
     */
    public static String concatWs(String deli, Object... values) {
        deli = Objects.requireNonNullElse(deli, "");
        ArrayList<String> ret = new ArrayList<>();
        if (values != null) {
            for (var value : values) {ret.add(value == null ? "" : value.toString());}
        }
        return String.join(deli, ret);
    }

    /**
     * 不带分隔符的字符串合并，null视为空，不会返回null。
     */
    public static <T> String join(Iterable<T> valueList) {
        return joinWs("", valueList);
    }

    /**
     * 带分隔符的字符串合并，null视为空，不会返回null。
     */
    public static <T> String joinWs(String deli, Iterable<T> valueList) {
        deli = Objects.requireNonNullElse(deli, "");
        ArrayList<String> ret = new ArrayList<>();
        if (valueList != null) {
            for (var value : valueList) {ret.add(value == null ? "" : value.toString());}
        }
        return String.join(deli, ret);
    }

    /**
     * 字符串分解（不会返回null）。null视为空。分解后的元素会删除首尾空格。
     */
    public static List<String> split(String deli, String src) {
        return split(deli, src, true);
    }

    /**
     * 字符串分解（不会返回null）。null视为空。
     */
    public static List<String> split(String deli, String src, Boolean trimSplit) {
        deli = deli == null ? "" : deli;
        src = src == null ? "" : src;
        List<String> ret = new ArrayList<>();
        if (deli.isEmpty()) {
            ret.add(src);
        } else {
            ret = Arrays.stream(src.split(Pattern.quote(deli), -1))
                    .map(x -> trimSplit ? x.trim() : x)
                    .collect(Collectors.toList());
        }
        return ret;
    }

    /**
     * 批量替换。大小写敏感。
     */
    public static String batchReplace(String text, List<CBatchReplaceItem> replaceList) {
        return batchReplace(text, priBatchReplaceList2Map(replaceList), false);
    }

    /**
     * 批量替换。大小写敏感。
     */
    public static String batchReplace(String text, List<CBatchReplaceItem> replaceList, Boolean ignoreCase) {
        return batchReplace(text, priBatchReplaceList2Map(replaceList), ignoreCase);
    }

    /**
     * 批量替换。大小写敏感。
     */
    public static String batchReplace(String text, Map<String, String> replaceMap) {
        return batchReplace(text, replaceMap, false);
    }

    /**
     * 批量替换。
     */
    public static String batchReplace(String text, Map<String, String> replaceMap, Boolean ignoreCase) {
        var ret = text;
        if (replaceMap != null && !replaceMap.isEmpty()) {
            var rep = replaceMap.keySet().stream().filter(x -> x != null && !x.isEmpty())
                    .collect(Collectors.toMap(x -> x, x -> Objects.requireNonNullElse(replaceMap.get(x), "")));
            if (!rep.isEmpty()) {
                var rgt = rep.keySet().stream().map(x -> "(" + Pattern.quote(x) + ")")
                        .collect(Collectors.joining("|"));
                var rgx = ignoreCase ? Pattern.compile(rgt, Pattern.CASE_INSENSITIVE) : Pattern.compile(rgt);
                ret = rgx.matcher(text).replaceAll(ma -> {
                    var key = ma.group();
                    var value = rep.keySet().stream()
                            .filter(y -> ignoreCase ? y.equalsIgnoreCase(key) : y.equals(key))
                            .map(x -> rep.get(x))
                            .findFirst().orElse("");
                    value = value.replace("\\", "\\\\");
                    value = value.replace("$", "\\$");
                    return value;
                });
            }
        }
        return ret;
    }

    // priBatchReplaceList2Map
    private static Map<String, String> priBatchReplaceList2Map(List<CBatchReplaceItem> replaceList) {
        replaceList = replaceList == null ? new ArrayList<>() : replaceList;
        Map<String, String> ret = new LinkedHashMap<>();
        replaceList.forEach(x -> ret.put(x.repFrom, x.repTo));
        return ret;
    }

    /**
     * 批量替换项。
     */
    public static class CBatchReplaceItem {
        /**
         * 待替换。
         */
        public final String repFrom;
        /**
         * 替换为。
         */
        public final String repTo;

        /**
         * 创建。
         */
        public CBatchReplaceItem(String repFrom, String repTo) {
            this.repFrom = repFrom;
            this.repTo = repTo;
        }

        /**
         * 创建。
         */
        public static CBatchReplaceItem create(String repFrom, String repTo) {
            return new CBatchReplaceItem(repFrom, repTo);
        }
    }

    /**
     * 将source的同名同类型字段值复制到target，并返回target。
     **/
    public static <TTarget, TSource> void shadowClone(TTarget target, TSource source) {
        if ((!Objects.isNull(target)) && (!Objects.isNull(source))) {
            var tClsT = target.getClass();
            var sClsT = source.getClass();
            var tFields = tClsT.getFields();
            var sFields = sClsT.getFields();
            for (var sField : sFields) {
                for (var tField : tFields) {
                    if (tField.getName().equals(sField.getName()) && (tField.getType() == sField.getType())) {
                        try {tField.set(target, sField.get(source));} catch (Exception ex) {var dummy = "";}
                    }
                }
            }
        }
    }

    /**
     * 映射class路径。
     */
    public static <T> String mapClassPath(String subPath) {
        Class<?> cls;
        try {
            var className = Thread.currentThread().getStackTrace()[2].getClassName();
            cls = Class.forName(className);
        } catch (Exception ex) {
            cls = SNE.class;
        }
        var ret = mapClassPath(subPath, cls);
        return ret;
    }

    /**
     * 映射class路径。
     */
    public static <T> String mapClassPath(String subPath, Class<T> tClass) {
        subPath = Objects.requireNonNull(subPath);
        subPath = subPath.replace("/", "\\");
        subPath = subPath.startsWith("\\") ? subPath.substring(1) : subPath;
        var ret = getClassDir(tClass) + subPath;
        return ret;
    }

    /**
     * 获取class所在的目录。
     */
    public static <T> String getClassDir(Class<T> tClass) {
        var ret = "";
        try {
            var classUrl = tClass.getResource("");
            assert classUrl != null;
            var classPath = classUrl.getPath();
            if (classPath != null) {
                ret = classPath.replace("/", "\\");
                ret = ret.startsWith("\\") ? ret.substring(1) : ret;
                ret = !ret.endsWith("\\") ? ret + "\\" : ret;
            }
        } catch (Exception ex) {
            ret = "";
        }
        return ret;
    }

    /**
     * 正则替换为内容转义。不会返回null。
     */
    public static String escapeRgxReplacement(String content) {
        var ret = "";
        if (content != null) {
            ret = content;
            ret = ret.replace("\\", "\\\\");
            ret = ret.replace("$", "\\$");
        }
        return ret;
    }

    /**
     * 生成List。与List.of类似，但是支持空值。
     */
    @SafeVarargs
    public static <TRecord> List<TRecord> ListOf(TRecord... records) {
        List<TRecord> ret = new ArrayList<>();
        if (records != null && records.length > 0) {ret = new ArrayList<>(Arrays.asList(records));}
        return ret;
    }

    /**
     * 实体字段值不为空的个数。
     */
    public static <TEntity> Integer notBlankCountOfEntity(TEntity entity) {
        var ret = 0;
        if (entity != null) {
            var cls = entity.getClass();
            var fields = cls.getFields();
            for (var field : fields) {
                try {
                    var fieldValue = field.get(entity);
                    if (fieldValue == null) {
                        ret = ret + 1;
                    } else {
                        if (fieldValue.getClass() == String.class) {
                            if (isBlank((String) fieldValue)) {
                                ret = ret + 1;
                            }
                        }
                    }
                } catch (Exception e) {
                    var dummy = "";
                }
            }
        }
        return ret;
    }

    /**
     * 格式化日期（自定义格式，y-年，M-月，d-日，H-时，m-分，s-秒，示例：yyyy-MM-dd）。
     */
    public static String formatDate(Date val, String format) {
        var ret = "";
        if (val != null && format != null) {
            var sd = new SimpleDateFormat(format);
            ret = sd.format(val);
        }
        return ret;
    }

    /**
     * 数值格式化（自定义格式示例: #.00表示保留2位小数）。
     */
    public static String formatNumber(Number val, String format) {
        var ret = "";
        if (val != null && format != null) {
            var df = new DecimalFormat(format);
            ret = df.format(val);
        }
        return ret;
    }

    /**
     * 查找resClass所在目录特定资源文件中的sql语句。注：特定资源文件是指[resClass包名称最后一级的名称.sql]。
     */
    public static String findPackResSql(String sqlRemarkTag, Class<?> resClass) {
        var rlt = "";
        try {
            var resNameList = findResNameList(resClass);
            var packNameLastList = Arrays.stream(resClass.getPackageName().split("\\.")).collect(Collectors.toList());
            var packNameLast = packNameLastList.get(packNameLastList.size() - 1);
            var resName = resNameList.stream()
                    .filter(x -> x.equalsIgnoreCase(packNameLast + ".sql"))
                    .findFirst().orElse("");
            var allSqlText = getResText(resClass, resName);
            rlt = findSqlByRemark(sqlRemarkTag, allSqlText);
        } catch (Exception ex) {
            rlt = "";
        }
        return rlt;
    }

    /**
     * 查找resClass所在目录特定资源文件中的sql语句。注：特定资源文件是指[resClass名称.sql]。
     */
    public static String findSelfResSql(Class<?> resClass, String sqlRemarkTag) {
        var rlt = "";
        try {
            var resNameList = findResNameList(resClass);
            var selfName = resClass.getSimpleName();
            var resName = resNameList.stream()
                    .filter(x -> x.equalsIgnoreCase(selfName + ".sql"))
                    .findFirst().orElse("");
            var allSqlText = getResText(resClass, resName);
            rlt = findSqlByRemark(sqlRemarkTag, allSqlText);
        } catch (Exception ex) {
            rlt = "";
        }
        return rlt;
    }

    /**
     * 查找resClass所在目录某个资源文件中的sql语句。resName为[文件名+扩展名]，大小写无关。
     */
    public static String findNamedResSql(Class<?> resClass, String resName, String sqlRemarkTag) {
        var rlt = "";
        try {
            var resNameList = findResNameList(resClass);
            var resNameEx = resNameList.stream()
                    .filter(x -> x.equalsIgnoreCase(resName))
                    .findFirst().orElse("");
            var allSqlText = getResText(resClass, resNameEx);
            rlt = findSqlByRemark(sqlRemarkTag, allSqlText);
        } catch (Exception ex) {
            rlt = "";
        }
        return rlt;
    }

    /**
     * 根据resource/jdbcsql/目录下的sql文件提取sql语句。
     * sql文件名称要与cla所在包的最后一级包名相同。
     */
    public static String findJdbcSQL(Class<?> cla, String remarkTag) {
        var ret = "";
        try {
            var packageNames = cla.getPackageName().split("\\.");
            var packageName = packageNames[packageNames.length - 1];
            var resName = "jdbcsql/" + packageName + ".sql";
            var inputStream = cla.getClassLoader().getResourceAsStream(resName);
            assert inputStream != null;
            var content = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
            ret = findSqlByRemark(remarkTag, content);
        } catch (IOException e) {
            var dummy = "";
        }
        return ret;
    }


    /**
     * 根据注释语句从多条sql语句集中提取单条sql语句。注释规矩参见示例。注：如果sqlRemarkTag不指定，则返回所有内容。
     */
    public static String findSqlByRemark(String sqlRemarkTag, String allSqlText) {
        var rlt = "";
        if (sqlRemarkTag == null || sqlRemarkTag.trim().isEmpty()) {
            rlt = allSqlText.trim();
        } else {
            if (!Objects.equals(allSqlText, "")) {
                var startPos = -1;
                var endPos = -1;
                var rgt = "\\/\\*\\s*(\\w+)\\s*\\*\\/";
                var pat = Pattern.compile(rgt);
                var ma = pat.matcher(allSqlText);
                while (ma.find()) {
                    var tag = ma.group(1);
                    if (Objects.equals(tag, sqlRemarkTag)) {
                        startPos = ma.end();
                    } else {
                        if (startPos > 0) {
                            endPos = ma.start();
                            break;
                        }
                    }
                }
                if (endPos < 0) {endPos = allSqlText.length();}
                if (startPos >= 0) {
                    rlt = allSqlText.substring(startPos, endPos).trim();
                    while (rlt.endsWith(";")) {rlt = rlt.replaceAll(";$", "").trim();}
                }
            }
        }
        return rlt;
    }

    /**
     * 查找resClass所在目录下的资源文件名称列表。结果中的文件名为：【文件基本名.扩展名称】。
     */
    public static List<String> findResNameList(Class<?> resClass) {
        List<String> resources = new ArrayList<>();
        var classLoader = resClass.getClassLoader();
        var resPath = resClass.getPackageName().replace(".", "/");
        var resourceURL = classLoader.getResource(resPath);
        if (resourceURL != null && "file".equals(resourceURL.getProtocol())) {
            var directory = new File(resourceURL.getPath());
            var files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (!file.isDirectory()) {
                        var fileName = file.getName();
                        if (!fileName.endsWith(".class")) {
                            resources.add(fileName);
                        }
                    }
                }
            }
        }
        return resources;
    }

    /**
     * 查找resClass所在目录下的资源文件文本内容。resName为【文件基本名称.扩展名称】。
     */
    public static String getResText(Class<?> resClass, String resName) {
        var rlt = "";
        if (resClass != null && resName != null) {
            try (var stream = resClass.getResourceAsStream(resName)) {
                assert stream != null;
                rlt = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
            } catch (Exception ex) {
                rlt = "";
            }
        }
        return rlt;
    }

    // 是否为空。[null,空,全空均视为空]
    public static Boolean blankIf(Object val) {
        var rlt = false;
        if (val == null) {rlt = true;} else if (val instanceof String) {
            if (((String) val).isBlank()) {rlt = true;}
        } else if (val instanceof Collection<?>) {if ((((Collection<?>) val).size() <= 0)) {rlt = true;}}
        return rlt;
    }

    // 为空替换。[null,空,全空均视为空]
    public static Object ifBlank(Object val, Object def) {
        var rlt = val;
        if (val == null) {rlt = def;} else if (val instanceof String) {
            if (((String) val).isBlank()) {rlt = def;}
        } else if (val instanceof Collection<?>) {if ((((Collection<?>) val).size() <= 0)) {rlt = def;}}
        return rlt;
    }

    // Map对象为空元素个数。[null,空,全空均视为空]
    public static Integer blankCountOfMap(Map<?, ?> map) {
        AtomicInteger rlt = new AtomicInteger(0);
        if (map != null) {
            map.keySet().forEach(k -> {
                var v = map.get(k);
                if (blankIf(v)) {rlt.set(rlt.get() + 1);}
            });
        }
        return rlt.get();
    }

    // Map对象为空元素个数。[null,空,全空均视为空]
    public static Integer notBlankCountOfMap(Map<?, ?> map) {
        AtomicInteger rlt = new AtomicInteger(0);
        if (map != null) {
            map.keySet().forEach(k -> {
                var v = map.get(k);
                if (!blankIf(v)) {rlt.set(rlt.get() + 1);}
            });
        }
        return rlt.get();
    }
}
